import simplejson
import os

from errorcodes import GeneralError
import loggable

"""
This module handles all the component related classes.
"""


class ComponentReader(loggable.Loggable):
  """
  This class is responsible for reading a component configuration.
  """
  
  def __init__(self, compName, _fd, logger):
    """
    Initialize the class with:
      fd: Name of the component config file open for read.
      logger: Logger to log into.
    """
      
    loggable.Loggable.__init__(self, logger)

    self.name = compName

    # Read the file but ignores the comment lines
    contents = ''.join([line for line in _fd.xreadlines()
        if line.find('#') == -1])

    try:
      data = simplejson.loads(contents)
    except ValueError:
      self.logger.raiseLog(GeneralError, 'Invalid format of "%s".' % self.name)

    version = data.get('version', None)
    if version == '0.1':
      self.version01()
    elif version == '0.2':
      # Future
      self.version02()
    else:
      self.logger.raiseLog(GeneralError, 'Component version unknown')

  def version01(self):
    """
    Reader for format version 0.1 .
    """
    pass

  def version02(self):
    """
    Reader for format version 0.2 .
    """
    pass


class ComponentManager(loggable.Loggable):
  """
  This class handles a directory of components. It's a component factory.
  """
  
  def __init__(self, compDir, logger):
    """
    Initialize the component manager and loads all the components with:
      compDIr: Components' directory to load.
      logger: Logger to log into.
    """
    
    loggable.Loggable.__init__(self, logger)

    self.components = {}

    for _file in os.listdir(compDir):
      # Check if file could be a component
      if not _file.endswith('.comp'):
        continue

      # Open the file for read
      compName = _file[:-5]
      try:
        _fd = open(os.path.join(compDir, _file))
      except IOError, msg:
        self.logger.warning('Failed to load component "%s": %s'
          % (compName, msg))
        continue

      # Try to create a component
      try:
        self.components[compName] = ComponentReader(compName, _fd, self.logger)
      except GeneralError, msg:
        self.logger.warning('Failed to load component "%s": %s'
            % (compName, msg))
      else:
        self.logger.debug('Component "%s" loaded successfully.')
      finally:
        _fd.close()

  def getComponentList(self):
    """
    Return the complete list of components loaded.
    """
    
    return self.components.keys()

  def getComponent(self, compName):
    """
    Try to instanciate a component of the specified file, returns that
    component.
    """
    
    try:
      reader = self.components[compName]
    except KeyError:
      self.logger.raiseLog(GeneralError, 'Unknown component "%s".' % compName)

    return Component(reader, self.logger)
      

class Component(loggable.Loggable):
  """
  Main component class. Offers the component as a black box.
  """

  def __init__(self, reader, logger):
    """
    Initialize the component with:
      reader: Reader instance.
      logger: Logger to log into.
    """
    # Base classes
    loggable.Loggable.__init__(self, logger)

    # Members
    self.reader = reader

  def getOpSignals(self, operation):
    """
    This method returns the operation signals for the component.
    """
    
    try:
      return self.reader.ops[operation]
    except KeyError:
      self.logger.raiseLog(GeneralError, 'Operation not supported by "%s"'
          % self.reader.name)

  def checkStateSignals(self, state):
    """
    This method returns the actual state of the component.
    """
    try:
      return self.reader.states[state]
    except KeyError:
      self.logger.raiseLog(GeneralError, \
          'State not supported by "%s"' % self.reader.name)

